package top.wshape1.shiziapi.uaa.security;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import jakarta.annotation.Resource;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import top.wshape1.shiziapi.common.constant.AuthConstant;
import top.wshape1.shiziapi.common.constant.BizCodeEnum;
import top.wshape1.shiziapi.common.constant.RedisConstant;
import top.wshape1.shiziapi.common.domain.to.UserTokenTo;
import top.wshape1.shiziapi.common.utils.JwtUtil;
import top.wshape1.shiziapi.common.utils.R;
import top.wshape1.shiziapi.uaa.config.ShiZiApiConfig;
import top.wshape1.shiziapi.uaa.domain.User;
import top.wshape1.shiziapi.uaa.feign.ThirdPartyFeignService;
import top.wshape1.shiziapi.uaa.mapper.UserMapper;
import top.wshape1.shiziapi.uaa.service.JwtService;
import top.wshape1.shiziapi.uaa.utils.ResponseUtil;
import top.wshape1.shiziapi.uaa.utils.UserUtil;

import java.io.IOException;
import java.util.Date;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author Wshape1
 * @version 1.0
 * @description
 * @since 2023-08-30
 */

@Component
@Slf4j
public class OAuth2LoginSuccessHandler implements AuthenticationSuccessHandler {

    @Resource
    private UserMapper userMapper;

    @Resource
    private ThirdPartyFeignService thirdPartyFeignService;

    @Resource
    private ThreadPoolExecutor threadPoolExecutor;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private ShiZiApiConfig shiZiApiConfig;

    @Resource
    private JwtService jwtService;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        System.out.println("OAuth2LoginSuccessHandler+===========================");
//        System.out.println(authentication);
        if (authentication instanceof OAuth2AuthenticationToken oAuth2AuthenticationToken) {

            OAuth2User oAuth2User = oAuth2AuthenticationToken.getPrincipal();

            Integer oauthId = oAuth2User.getAttribute("id");
            assert oauthId != null;
            String[] split = request.getServletPath().split("/");
            String regId = split[split.length - 1];

            String header = request.getHeader(AuthConstant.HEADER_PAYLOAD_NAME);

            String token;

            // 进行OAuth2账号绑定
            if (header != null) {
                //  账号绑定

                User one = userMapper.selectOne(new LambdaQueryWrapper<User>()
                        .eq("gitee".equalsIgnoreCase(regId), User::getGiteeId, oauthId)
                        .eq("github".equalsIgnoreCase(regId), User::getGithubId, oauthId)
                );

                if (one != null) {
                    ResponseUtil.writeResponse(response, R.instance(BizCodeEnum.EXIST_BINDING_ERROR));
                    return;
                }

                token = bindingOAuth2(request, header, regId, oauthId, oAuth2User);


            } else {
                // 正常登录流程
                token = loginOrReg(regId, oauthId, oAuth2User);
            }


            // 登录
            response.setHeader(AuthConstant.HEADER_NEW_TOKEN_NAME, token);
            ResponseUtil.writeResponse(response, R.ok());
        }
    }

    private String bindingOAuth2(HttpServletRequest request, String tokenInfo, String regId, Integer oauthId, OAuth2User oAuth2User) {
        String name = oAuth2User.getAttribute("login");
        User user = UserUtil.getFromEncodedString(tokenInfo);

        User update = new User();
        update.setId(user.getId());

        if ("gitee".equalsIgnoreCase(regId)) {
            update.setGiteeId(oauthId.longValue());
            update.setGiteeName(name);
        } else {
            update.setGithubId(oauthId.longValue());
            update.setGithubName(name);
        }

        userMapper.updateById(update);

        String oldToken = request.getHeader(HttpHeaders.AUTHORIZATION).replace("Bearer ", "");

        UserTokenTo userTokenTo = jwtService.renewJwt(user.getId(), oldToken);
        return userTokenTo.getNewToken();
    }

    private String loginOrReg(String regId, Integer oauthId, OAuth2User oAuth2User) throws IOException {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        if ("gitee".equalsIgnoreCase(regId)) {
            queryWrapper.eq(User::getGiteeId, oauthId);
        } else {
            queryWrapper.eq(User::getGithubId, oauthId);
        }
        User one = userMapper.selectOne(queryWrapper);

        // 如果该用户第一次登录，则注册
        if (one == null) {
            String name = oAuth2User.getAttribute("login");
            String avatarUrl = oAuth2User.getAttribute("avatar_url");

            // 获取用户对象
            User user = getUser(name, regId, oauthId);
            // 保存用户
            // 保存到数据库后 会返回id给user
            userMapper.insert(user);

            // 调用远程服务 上传头像
            R<Object> r = thirdPartyFeignService.uploadAvatarByUrl(user.getId(), avatarUrl);
            // 异步保存头像地址到数据库
            asyncSaveAvatar(r, user);


            one = user;
        }

        // 创建jwt
        one.setPassword(null);
        String idStr = one.getId().toString();
        String token = JwtUtil.createJWT(idStr, JSON.toJSONString(one), shiZiApiConfig.SIGN_KEY);

        // 存入Redis 并设置过期时间
        redisTemplate.opsForValue().set(RedisConstant.USER_JWT_KEY + idStr, token, shiZiApiConfig.DURATION_DAYS, TimeUnit.DAYS);

        return token;
    }

    private User getUser(String name, String regId, Integer oauthId) {
        User user = new User();
        Date date = new Date();
        user.setStatus(1);
        user.setCreateTime(date);
        user.setUpdateTime(date);
        user.setNickname(name);
        if ("gitee".equalsIgnoreCase(regId)) {
            user.setGiteeId(oauthId.longValue());
            user.setGiteeName(name);
        } else if ("github".equalsIgnoreCase(regId)) {
            user.setGithubId(oauthId.longValue());
            user.setGithubName(name);
        }
        return user;
    }

    private void asyncSaveAvatar(R<Object> r, User user) {
        if (r.isSuccessful()) {
            // 设置用户头像
            user.setAvatarUrl(r.getMsg());
            // 异步保存用户头像
            CompletableFuture.runAsync(() -> {
                try {
                    String aUrl = r.getMsg();
                    User update = new User();
                    update.setId(user.getId());
                    update.setAvatarUrl(aUrl);
                    userMapper.updateById(update);
                } catch (Exception e) {
                    log.error("第三方注册用户{}，出现了错误：{}", user.getId(), e.getMessage());
                }
            }, threadPoolExecutor);

        } else {
            log.error("第三方注册用户{}，出现了远程服务错误：{}", user.getId(), r.getData());
        }
    }
}
